home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
PC World Komputer 2007 December
/
PCWKCD1207B.iso
/
Blogowanie poza sfera
/
Flock 1.0 beta
/
flock-1.0RC3.en-US.win32.exe
/
flock
/
components
/
flockWebServiceManager.js
< prev
next >
Wrap
Text File
|
2007-10-18
|
25KB
|
696 lines
// vim: ts=2 sw=2 expandtab cindent
//
// BEGIN FLOCK GPL
//
// Copyright Flock Inc. 2005-2007
// http://flock.com
//
// This file may be used under the terms of of the
// GNU General Public License Version 2 or later (the "GPL"),
// http://www.gnu.org/licenses/gpl.html
//
// Software distributed under the License is distributed on an "AS IS" basis,
// WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
// for the specific language governing rights and limitations under the
// License.
//
// END FLOCK GPL
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cr = Components.results;
const WEBSVCMGR_CID = Components.ID("{23d157f0-5941-11db-b0de-0800200c9a66}");
const WEBSVCMGR_CONTRACTID = "@flock.com/webservice-manager;1";
//const OBS_TOPIC_FLOCKDATAREADY = "flock-data-ready";
const OBS_TOPIC_FLOCKDOCREADY = "FlockDocumentReady";
const OBS_TOPIC_FORMSUBMIT = "earlyformsubmit";
const OBS_TOPIC_XPCOMSHUTDOWN = "xpcom-shutdown";
// ========================================================
// ========== BEGIN flockWebServiceManager class ==========
// ========================================================
function flockWebServiceManager()
{
this._logger = Cc["@flock.com/logger;1"].createInstance(Ci.flockILogger);
this._logger.init("webServiceManager");
this._logger.info("Created Web Service Manager Object");
// Associative array where keys are service contract IDs and values are URNs
this.mAllServicesByContract = [];
// Associative array where keys are URNs and values are XPCOM services
this.mInstantiatedServices = [];
// Cache the .domains properties of each service, by URN
this.mDomains = [];
// mTempIgnore is used to keep track of which services the user has elected
// NOT to enable Flock support for during this browser session
this.mTempIgnore = [];
// mListeners is the array of flockIWebServiceAccountListeners
this.mListeners = [];
// mKeepAccounts is an associative array of service URNs to dates, used by
// autoKeepAccountForService()
this.mKeepAccounts = [];
this.obs = Cc["@mozilla.org/observer-service;1"]
.getService(Ci.nsIObserverService);
//this.obs.addObserver(this, OBS_TOPIC_FLOCKDATAREADY, false);
this.obs.addObserver(this, OBS_TOPIC_XPCOMSHUTDOWN, false);
// addObserver calls for FLOCKDOCREADY and FORMSUBMIT are in this.setup()
this.mEnabled = true;
this.mRegisteredEvents = false;
this._metrics = Cc["@flock.com/metrics-service;1"]
.getService(Ci.flockIMetricsService);
this.setup();
}
// BEGIN nsISupports interface
flockWebServiceManager.prototype.QueryInterface =
function flockWebServiceManager_QueryInterface(aIID)
{
if ( !aIID.equals(Ci.nsISupports) &&
!aIID.equals(Ci.nsIObserver) &&
!aIID.equals(Ci.flockIWebServiceManager) )
{
throw Cr.NS_ERROR_NO_INTERFACE;
}
return this;
}
// END nsISupports interface
// BEGIN nsIObserver interface
flockWebServiceManager.prototype.observe =
function flockWebServiceManager_observe(aSubject, aTopic, aData)
{
switch (aTopic) {
//case OBS_TOPIC_FLOCKDATAREADY:
//this.obs.removeObserver(this, OBS_TOPIC_FLOCKDATAREADY);
//this.setup();
//break;
case OBS_TOPIC_FORMSUBMIT:
this.onFormSubmit(aSubject, aData);
break;
case OBS_TOPIC_FLOCKDOCREADY:
this.onDocumentReady(aSubject, aData);
break;
case OBS_TOPIC_XPCOMSHUTDOWN:
if (this.mRegisteredEvents) {
this.obs.removeObserver(this, OBS_TOPIC_FLOCKDOCREADY);
this.obs.removeObserver(this, OBS_TOPIC_FORMSUBMIT);
}
this.obs.removeObserver(this, OBS_TOPIC_XPCOMSHUTDOWN);
break;
}
}
// END nsIObserver interface
// BEGIN flockIWebServiceManager interface
flockWebServiceManager.prototype.registerWebService =
function flockWebServiceManager_registerWebService(aService)
{
aService.QueryInterface(Ci.flockIManageableWebService);
this._logger.info(".registerWebService(contract: "+aService.contractId+" URN: "+aService.urn+")");
this.mAllServicesByContract[aService.contractId] = aService.urn;
this.mInstantiatedServices[aService.urn] = aService;
this.checkFirstRun(aService);
this.checkPasswordsForService(aService);
}
flockWebServiceManager.prototype.unregisterWebService =
function flockWebServiceManager_unregsiterWebService(aService)
{
aService.QueryInterface(Ci.flockIManageableWebService);
this._logger.info(".unregisterWebService("+aService.urn+")");
this.mInstantiatedServices[aService.urn] = undefined;
}
flockWebServiceManager.prototype.addListener =
function flockWebServiceManager_addListener(aListener)
{
aListener.QueryInterface(Ci.flockIWebServiceAccountListener);
this.mListeners.push(aListener);
this._logger.info(".addListener() ["+this.mListeners.length+"]");
}
flockWebServiceManager.prototype.removeListener =
function flockWebServiceManager_removeListener(aListener)
{
for (var i = 0; i < this.mListeners.length; ++i) {
if (aListener == this.mListeners[i]) {
this.mListeners.splice(i, 1);
break;
}
}
}
flockWebServiceManager.prototype.autoKeepAccountForService =
function flockWebServiceManager_autoKeepAccountForService(aServiceURN)
{
var stopTime = new Date();
stopTime.setHours(stopTime.getHours() + 1);
this.mKeepAccounts[aServiceURN] = stopTime.getTime();
}
// END flockIWebServiceManager interface
// BEGIN internal helper functions
flockWebServiceManager.prototype.setup =
function flockWebServiceManager_setup()
{
this._logger.info(".setup()");
this.coop = Cc["@flock.com/singleton;1"]
.getService(Ci.flockISingleton)
.getSingleton("chrome://flock/content/common/load-faves-coop.js")
.wrappedJSObject;
this.acUtils = Cc["@flock.com/account-utils;1"]
.getService(Ci.flockIAccountUtils);
this._profiler = Cc["@flock.com/profiler;1"].getService(Ci.flockIProfiler);
var prefSvc = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefService);
var prefs = prefSvc.getBranch(null);
this.firstRunPrefs = prefSvc.getBranch("flock.service.firstruncomplete.");
this.svcConfPrefs = prefSvc.getBranch("flock.service.confirmation.");
this.mEnabled = true;
if (prefs.getPrefType("flock.service.webservicemanager.enabled")) {
this.mEnabled = prefs.getBoolPref("flock.service.webservicemanager.enabled");
}
if (!this.mEnabled) { return; }
this.obs.addObserver(this, OBS_TOPIC_FORMSUBMIT, false);
this.obs.addObserver(this, OBS_TOPIC_FLOCKDOCREADY, false);
this.mRegisteredEvents = true;
// Web Service Manager is responsible for determining when to instantiate any
// services in the "wsm-startup" category.
var catMgr = Cc["@mozilla.org/categorymanager;1"].getService(Ci.nsICategoryManager);
var svcEnum = catMgr.enumerateCategory("wsm-startup");
var firstRun = ( !this.firstRunPrefs.getPrefType("webservicemanager") ||
!this.firstRunPrefs.getBoolPref("webservicemanager") );
this._logger.info((firstRun) ? "First run" : "NOT first run");
while (svcEnum.hasMoreElements()) {
var entry = svcEnum.getNext().QueryInterface(Ci.nsISupportsCString);
if (!entry) continue;
var contractID = catMgr.getCategoryEntry("wsm-startup", entry.data);
this._logger.info("checking "+contractID);
// Always instantiate web services components on first run, or when an
// account has already been configured
var shouldInstantiate = ( firstRun ||
this.coop.Account.find({serviceId: contractID}).length );
var findSvc;
if (!shouldInstantiate) {
// If it doesn't exist in the RDF, then it was not properly initialized
// last time, so make sure it gets instantiated now
findSvc = this.coop.Service.find({serviceId: contractID});
if (findSvc.length == 0) { shouldInstantiate = true; }
}
if (shouldInstantiate) {
var service = this.instantiateWebService(contractID);
this.checkFirstRun(service);
} else {
// There's still a possibility that this is the first run for this
// service -- eg. if it's from an extention
this._logger.info("Decided NOT to instantiate "+contractID);
this.mAllServicesByContract[contractID] = findSvc[0].id();
this.checkFirstRun(null, contractID);
}
}
this.firstRunPrefs.setBoolPref("webservicemanager", true);
}
/**
* Every web service component must get instantiated at least once - generally
* on first-run - so that it can update the RDF, etc.
*/
flockWebServiceManager.prototype.checkFirstRun =
function flcokWebServiceManager_checkFirstRun(aService, aServiceContractID)
{
var serviceURN = (aService) ? aService.urn
: this.coop.Service.find({serviceId: aServiceContractID})[0].id();
this._logger.info(".checkFirstRun() "+serviceURN);
if ( !this.firstRunPrefs.getPrefType(serviceURN) ||
!this.firstRunPrefs.getBoolPref(serviceURN) )
{
// This is the first time we are running with this service, so ensure
// that it is instantiated
if (!aService) {
aService = this.instantiateWebService(aServiceContractID);
}
this.firstRunPrefs.setBoolPref(serviceURN, true);
}
}
flockWebServiceManager.prototype.instantiateWebService =
function flockWebServiceManager_instantiateWebService(aServiceContractID)
{
this._logger.info(".instantiateWebService('"+aServiceContractID+"')");
var service = Cc[aServiceContractID]
.getService(Ci.flockIManageableWebService);
this.mAllServicesByContract[aServiceContractID] = service.urn;
this.mInstantiatedServices[service.urn] = service;
this.checkPasswordsForService(service);
return service;
}
flockWebServiceManager.prototype.checkPasswordsForService =
function flockWebServiceManager_checkPasswordsForService(aService)
{
if (aService.needPassword) {
// This service requires that we have a password stored in order to have
// a properly configured account. Check if we have accounts, and if they
// don't have passwords then log them out in order to force the user to
// log in again.
var c_accounts = this.coop.Account.find({serviceId: aService.contractId});
var missingAPassword = false;
for (var i = 0; i < c_accounts.length; i++) {
var c_acct = c_accounts[i];
var pw = this.acUtils.getPassword(aService.urn+":"+c_acct.accountId);
if (!pw) {
missingAPassword = true;
if (c_acct.isAuthenticated) {
aService.logout();
this.acUtils.markAllAccountsAsLoggedOut(aService.contractId);
return;
}
}
}
if (missingAPassword) {
// We are missing a password, but we don't know whether that account is
// currently logged in or not. If it's an account that the user never
// uses, then we don't want to force logout here (because we could be
// logging them out of the account they actually DO use)
if (c_accounts.length == 1) {
// But in this case there's only one account. This must be the one
// we're missing the password for. So we want to log the service out,
// regardless. This will force the user to log in again, so we can
// re-capture that password.
aService.logout();
this.acUtils.markAllAccountsAsLoggedOut(aService.contractId);
}
}
}
}
flockWebServiceManager.prototype.onFormSubmit =
function flockWebServiceManager_onFormSubmit(aForm, aData)
{
this._logger.info(".onFormSubmit('"+aData+"')");
var url = aForm.ownerDocument.QueryInterface(Ci.nsIDOMHTMLDocument).URL;
if (!this.mIOS) {
this.mIOS = Cc["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
}
var uri = this.mIOS.newURI(url, null, null);
for (var contractID in this.mAllServicesByContract) {
this._logger.info("try "+contractID);
// Check the domain
var serviceURN = this.mAllServicesByContract[contractID];
this._logger.info(" - URN is "+serviceURN);
if (!this.testDomains(serviceURN, uri.host)) {
this._logger.info("NO MATCH on domains for "+serviceURN);
continue;
}
this._logger.info("form domains MATCH for "+serviceURN);
// The domain matched, so ensure this service is instantiated
var service = this.mInstantiatedServices[serviceURN];
if (!service) {
service = this.instantiateWebService(contractID);
}
// Ask the service if it owns this form being submitted
if (!service.ownsLoginForm(aForm)) { continue; }
this._logger.info(" service '"+service.urn+"' says it owns this form.");
var pw = service.getCredentialsFromForm(aForm);
if (pw) {
// The user has just tried to log in, sign up for, or change their
// password on this service. We don't know which account it was for,
// or if they were successful yet -- so for now, store the password
// temporarily, associated with the service URN.
this._logger.info(" saving TEMPORARY password for '"+service.urn+"'");
var origin = pw.QueryInterface(Ci.flockIPassword);
this.acUtils.setTempPassword(service.urn, pw.user, pw.password, origin.formType);
} else {
this._logger.info(" service'"+service.urn+"' could not retrieve login info, nothing we can do :(");
}
// This form has already been claimed by a service, don't give the other
// services a chance to claim it.
break;
}
}
flockWebServiceManager.prototype.onDocumentReady =
function flockWebServiceManager_onDocumentReady(aDocument, aURL)
{
var profilerEvt = this._profiler.profileEventStart("onDocumentReady");
this._logger.info(".onDocumentReady('"+aURL+"')");
// flockIManageableWebServices only know how to deal with HTML documents
if (!(aDocument instanceof Ci.nsIDOMHTMLDocument)) {
this._profiler.profileEventEnd(profilerEvt, aURL);
return;
}
if (!this.mIOS) {
this.mIOS = Cc["@mozilla.org/network/io-service;1"]
.getService(Ci.nsIIOService);
}
var uri = this.mIOS.newURI(aURL, null, null);
var isOwned = false;
// Function to alert user if there's a need to re-login
var inst = this;
var notifyPasswordMissing = function wsm_passwordMissing(aService) {
for (var i = 0; i < inst.mListeners.length; ++i) {
if (inst.mListeners[i].onAccountPasswordMissing) {
inst.mListeners[i].onAccountPasswordMissing(aService, aDocument);
}
}
};
// Loop through all the services
for (var contractID in this.mAllServicesByContract) {
this._logger.info("try "+contractID);
// Check the domain
var serviceURN = this.mAllServicesByContract[contractID];
this._logger.info(" - URN is "+serviceURN);
if (!this.testDomains(serviceURN, uri.host)) { continue; }
this._logger.info("domains MATCH for "+serviceURN);
// The domain matched, so ensure this service is instantiated
var service = this.mInstantiatedServices[serviceURN];
if (!service) {
service = this.instantiateWebService(contractID);
}
// Ask the service if it actually cares about this document
if (!service.ownsDocument(aDocument)) { continue; }
this._logger.info(" service '"+service.urn+"' says IT OWNS this document.");
isOwned = true;
// We now know the service cares about this document, so next check if
// it's a login landing page
if (service.docRepresentsSuccessfulLogin(aDocument)) {
this._logger.info(" service '"+service.urn+"' says this page represents a successful login!");
// User has just logged in -- see which account
var acct_id = service.getAccountIDFromDocument(aDocument);
if (acct_id) {
if (this.getServiceConfirmation(service.urn)) {
// We have detected an account ID on this page, and furthermore the
// service confirmation has not forbade us from trying to use this
// service. So lets's see if we need to create an account.
// If we have a temp password stored for this service (from the
// onFormSubmit call), then we now know that we can associate it with
// this account ID
var pw = this.acUtils.getTempPassword(service.urn);
if (pw) {
var origin = pw.QueryInterface(Ci.flockIPassword);
if (origin.formType == "signup") {
this._metrics.report("Account-Signup", service.shortName);
}
this.acUtils.setTempPassword(service.urn + ":" + acct_id, pw.user,
pw.password, origin.formType);
this.acUtils.clearTempPassword(service.urn);
}
// The service has told us the account ID represented by the
// document, but that account might not actually exist in the RDF.
var acctURN = this.acUtils.getAccountURNById(service.urn, acct_id);
if (!acctURN) {
// Flock does not yet know about this account
if (pw || !service.needPassword) {
// We have a password, or else this service does not require a
// password in order for us to configure an account.
this._logger.info(" set up new temporary account: " + acct_id);
this.setUpAccount(service, acct_id, aDocument);
} else {
// We don't have a stored password. That may be due to migrated
// cookies (bug 6404).
this._logger.info(" no password associated to the account");
notifyPasswordMissing(service);
}
} else if (this.acUtils.isTransient(acctURN)) {
// The account is set up, but transient. We should give the user
// the option to keep it, unless they have already explicitly
// dismissed that notification.
this._logger.info(" found existing temp account: " + acct_id);
this.checkNotifyAccount(aDocument, service.getAccount(acctURN));
} else {
// The account is already set up and is non-transient. The only
// thing to do is make sure we don't have an out-of-date password.
this._logger.info(" found existing account: "+acct_id);
if (pw) {
this.acUtils.makeTempPasswordPermanent(service.urn+":"+acct_id);
}
}
} else {
this._logger.info(" user indicated to ignore this service");
}
} else {
this._logger.info(" unable to find account ID on this page");
// This is a somewhat broken state. We know we're logged in, but we
// can't tell which account. Our only hope is to make the user log
// out and log in again. Using the "no password" notification to do
// that.
notifyPasswordMissing(service);
}
}
// Give the service a chance to update the auth state of its account(s)
service.updateAccountStatusFromDocument(aDocument);
// Give media services a chance to detect media
var isMediaService = true;
try {
service.QueryInterface(Ci.flockIMediaWebService);
} catch (ex) {
isMediaService = false;
}
if (isMediaService) {
service.decorateForMedia(aDocument);
}
this._profiler.profileEventEnd(profilerEvt, aURL);
return;
}
// If no service owns the doc, notify explore button menu to redraw
if (!isOwned) {
this.obs.notifyObservers(aDocument, "media", "media:update");
}
this._profiler.profileEventEnd(profilerEvt, aURL);
}
flockWebServiceManager.prototype.testDomains =
function flockWebServiceManager_testDomains(aServiceURN, aHost)
{
var domains = this.mDomains;
if (!domains[aServiceURN]) {
var domainList = this.coop.get(aServiceURN).domains;
var domainArr = [];
if (domainList) {
this._logger.info("got domains for '"+aServiceURN+"': "+domainList);
domains[aServiceURN] = domainList.split(",");
}
}
var domainArr = domains[aServiceURN];
if (!domainArr || domainArr.length == 0) {
return true;
}
for (var i = 0; i < domainArr.length; i++) {
var domain = domainArr[i];
if (aHost == domain) return true;
var idx = aHost.indexOf("."+domain);
if ((idx > 0) && ((idx + domain.length + 1) == aHost.length)) return true;
}
this._logger.info("domains did not match for "+aServiceURN);
return false;
}
flockWebServiceManager.prototype.getServiceConfirmation =
function flockWebServiceManager_getServiceConfirmation(aURN)
{
if (this.mTempIgnore[aURN]) {
return false;
}
var askUser = true; // default value is to ask the user
if (this.svcConfPrefs.getPrefType(aURN)) {
askUser = this.svcConfPrefs.getBoolPref(aURN);
}
return askUser;
}
flockWebServiceManager.prototype.setServiceConfirmation =
function flockWebServiceManager_setServiceConfirmation(aUrn, aValue)
{
this.svcConfPrefs.setBoolPref(aUrn, aValue);
}
flockWebServiceManager.prototype.setUpAccount =
function flockWebServiceManager_setUpAccount(aService, aNewAccountID, aDocument)
{
this._logger.info("setUpAccount('"+aService.shortName+"', '"+aNewAccountID+"')");
var inst = this;
var authListener = {
onSuccess: function (aAccount, aTopic) {
inst.notifyListeners(aAccount, "accountAuthorized");
},
onError: function (aSubject, aTopic, aError) {
inst._logger.debug("setUpAccount() authListener.onError()");
}
};
var creationListener = {
onSuccess: function (aAccount, aTopic) {
aAccount.QueryInterface(Ci.flockIWebServiceAccount);
inst.checkNotifyAccount(aDocument, aAccount);
aAccount.activate(authListener);
},
onError: function (aSubject, aTopic, aError) {
inst._logger.debug("setUpAccount() creationListener.onError()");
}
};
// Accounts are created as 'transient' by default, unless
// autoKeepAccountForService() has been called recently
var setTransient = true;
var stopTime = this.mKeepAccounts[aService.urn];
if (stopTime) {
var nowTime = (new Date()).getTime();
if (nowTime < stopTime) {
setTransient = false;
} else {
this.mKeepAccounts[aService.urn] = undefined;
}
}
var acct = aService.addAccountById(aNewAccountID, setTransient, creationListener);
this._metrics.report("Account-Add", aService.shortName);
if (!setTransient) {
acct.keep();
}
this._logger.info("acct is: "+acct);
}
flockWebServiceManager.prototype.checkNotifyAccount =
function flockWebServiceManager_checkNotifyAccount(aDocument, aAccount)
{
aDocument.QueryInterface(Ci.nsIDOMDocument);
aAccount.QueryInterface(Ci.flockIWebServiceAccount);
this._logger.info("checkNotifyAccount('"+aAccount.urn+"')");
this.notifyListeners(aAccount, "accountCreated", aDocument);
}
flockWebServiceManager.prototype.notifyListeners =
function flockWebServiceManager_notifyListeners(aAccount, aEventName, aDocument)
{
switch (aEventName) {
case "accountCreated":
for (var i = 0; i < this.mListeners.length; ++i) {
this.mListeners[i].onAccountCreated(aAccount, aDocument);
}
break;
case "accountPasswordMissing":
for (var i = 0; i < this.mListeners.length; ++i) {
if (this.mListeners[i].onAccountPasswordMissing)
this.mListeners[i].onAccountPasswordMissing(null, aDocument);
}
break;
case "accountAuthorized":
for (var i = 0; i < this.mListeners.length; ++i) {
this.mListeners[i].onAccountAuthChange(aAccount);
}
break;
default:
break;
}
}
// ========== END flockWebServiceManager class ==========
// ========== BEGIN XPCOM Module support ==========
// BEGIN flockWebServiceManagerModule object
var flockWebServiceManagerModule = {};
flockWebServiceManagerModule.registerSelf =
function flockWebServiceManagerModule_registerSelf(compMgr, fileSpec, location, type)
{
compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar);
compMgr.registerFactoryLocation( WEBSVCMGR_CID,
"Flock Web Service Manager JS Component",
WEBSVCMGR_CONTRACTID,
fileSpec,
location,
type );
}
flockWebServiceManagerModule.getClassObject =
function flockWebServiceManagerModule_getClassObject(compMgr, cid, iid)
{
if (!cid.equals(WEBSVCMGR_CID))
throw Cr.NS_ERROR_NO_INTERFACE;
if (!iid.equals(Ci.nsIFactory))
throw Cr.NS_ERROR_NOT_IMPLEMENTED;
return flockWebServiceManagerFactory;
}
flockWebServiceManagerModule.canUnload =
function flockWebServiceManagerModule_canUnload(compMgr)
{
return true;
}
// END flockWebServiceManagerModule object
// BEGIN flockWebServiceManagerFactory object
var flockWebServiceManagerFactory = new Object();
flockWebServiceManagerFactory.createInstance =
function flockWebServiceManagerFactory_createInstance(outer, iid)
{
if (outer != null) {
throw Cr.NS_ERROR_NO_AGGREGATION;
}
return (new flockWebServiceManager()).QueryInterface(iid);
}
// END flockWebServiceManagerFactory object
// NS module entry point
function NSGetModule(compMgr, fileSpec) {
return flockWebServiceManagerModule;
}
// ========== END XPCOM module support ==========